package io.gitee.zhangbinhub.admin.log.controller

import cn.dev33.satoken.annotation.SaCheckPermission
import cn.dev33.satoken.annotation.SaCheckRole
import com.fasterxml.jackson.databind.ObjectMapper
import io.gitee.zhangbinhub.acp.boot.exceptions.WebException
import io.gitee.zhangbinhub.acp.boot.log.LogAdapter
import io.gitee.zhangbinhub.acp.core.common.CalendarTools
import io.gitee.zhangbinhub.acp.core.common.CommonTools
import io.gitee.zhangbinhub.admin.common.constant.ModuleFuncCode
import io.gitee.zhangbinhub.admin.common.constant.RoleCode
import io.gitee.zhangbinhub.admin.common.vo.CustomerQueryPageVo
import io.gitee.zhangbinhub.admin.log.base.BaseLogEntity
import io.gitee.zhangbinhub.admin.log.constant.LogApi
import io.gitee.zhangbinhub.admin.log.constant.LogConstant
import io.gitee.zhangbinhub.admin.log.po.FileDownLoadPo
import io.gitee.zhangbinhub.admin.log.po.LogQueryPo
import io.gitee.zhangbinhub.admin.log.service.LogFileService
import io.gitee.zhangbinhub.admin.log.service.LogService
import io.gitee.zhangbinhub.admin.log.vo.LoginLogVo
import io.gitee.zhangbinhub.admin.resource.server.base.BaseResourceServerController
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.Parameter
import io.swagger.v3.oas.annotations.responses.ApiResponse
import io.swagger.v3.oas.annotations.responses.ApiResponses
import io.swagger.v3.oas.annotations.tags.Tag
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import jakarta.validation.Valid
import jakarta.validation.constraints.NotBlank
import jakarta.validation.constraints.NotNull
import org.bouncycastle.util.encoders.Base64
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.http.MediaType
import org.springframework.http.ResponseEntity
import org.springframework.validation.annotation.Validated
import org.springframework.web.bind.annotation.*
import java.io.File

/**
 * @since JDK 11
 */
@Validated
@RestController
@RequestMapping(LogApi.basePath)
@Tag(name = "日志信息")
class LogController @Autowired
constructor(
    private val logAdapter: LogAdapter,
    private val logFileService: LogFileService,
    private val objectMapper: ObjectMapper,
    private val logService: LogService
) : BaseResourceServerController(logAdapter) {

    @Operation(summary = "获取各应用过去3个月的登录次数统计")
    @ApiResponses(ApiResponse(responseCode = "400", description = "没有权限做此操作；"))
    @SaCheckPermission(ModuleFuncCode.sysMonitor, orRole = [RoleCode.SUPER])
    @GetMapping(value = [LogApi.loginInfo], produces = [MediaType.APPLICATION_JSON_VALUE])
    @Throws(WebException::class)
    fun findLoginLog(): ResponseEntity<List<LoginLogVo>> =
        CalendarTools.getNowDateTime().withTimeAtStartOfDay().minusMonths(LogConstant.LOGIN_LOG_STATISTICS_MAX_MONTH)
            .let {
                ResponseEntity.ok(logService.loginStatistics(it.millis))
            }

    @Operation(
        summary = "查询路由日志列表",
        description = "查询条件：客户端ip、网关ip、请求路径、路由服务id、应用名称、用户名称、开始时间、结束时间、响应状态"
    )
    @ApiResponses(ApiResponse(responseCode = "400", description = "参数校验不通过；"))
    @SaCheckPermission(ModuleFuncCode.sysMonitor, orRole = [RoleCode.SUPER])
    @PostMapping(value = [LogApi.gateWayRouteLog], produces = [MediaType.APPLICATION_JSON_VALUE])
    @Throws(WebException::class)
    fun queryRouteLog(@RequestBody @Valid logQueryPo: LogQueryPo): ResponseEntity<CustomerQueryPageVo<out BaseLogEntity>> =
        ResponseEntity.ok(logService.doQueryRouteLog(logQueryPo))

    @Operation(
        summary = "查询操作日志列表",
        description = "查询条件：客户端ip、网关ip、请求路径、路由服务id、应用名称、用户名称、开始时间、结束时间"
    )
    @ApiResponses(ApiResponse(responseCode = "400", description = "参数校验不通过；"))
    @SaCheckPermission(ModuleFuncCode.sysMonitor, orRole = [RoleCode.SUPER])
    @PostMapping(value = [LogApi.operateLog], produces = [MediaType.APPLICATION_JSON_VALUE])
    @Throws(WebException::class)
    fun queryOperateLog(@RequestBody @Valid logQueryPo: LogQueryPo): ResponseEntity<CustomerQueryPageVo<out BaseLogEntity>> =
        ResponseEntity.ok(logService.doQueryOperateLog(logQueryPo))

    @Operation(
        summary = "查询登录日志列表",
        description = "查询条件：客户端ip、网关ip、请求路径、路由服务id、应用名称、用户名称、开始时间、结束时间"
    )
    @ApiResponses(ApiResponse(responseCode = "400", description = "参数校验不通过；"))
    @SaCheckPermission(ModuleFuncCode.sysMonitor, orRole = [RoleCode.SUPER])
    @PostMapping(value = [LogApi.loginLog], produces = [MediaType.APPLICATION_JSON_VALUE])
    @Throws(WebException::class)
    fun queryLoginLog(@RequestBody @Valid logQueryPo: LogQueryPo): ResponseEntity<CustomerQueryPageVo<out BaseLogEntity>> =
        ResponseEntity.ok(logService.doQueryLoginLog(logQueryPo))

    @Operation(summary = "查询指定日期范围的日志备份文件", description = "查询条件：开始日期、结束日期")
    @ApiResponses(ApiResponse(responseCode = "400", description = "参数校验不通过；"))
    @SaCheckRole(RoleCode.SUPER)
    @PostMapping(value = [LogApi.logFile], produces = [MediaType.APPLICATION_JSON_VALUE])
    @Throws(WebException::class)
    fun queryFile(
        @Parameter(description = "开始日期", required = true, example = "0")
        @NotNull(message = "开始日期不能为空")
        @RequestParam(name = "startDate")
        startDate: Long,
        @Parameter(description = "结束日期", required = true, example = "0")
        @NotNull(message = "结束日期不能为空")
        @RequestParam(name = "endDate")
        endDate: Long
    ): ResponseEntity<List<String>> =
        try {
            val start = longToDate(startDate)
            val end = longToDate(endDate)
            val nowDay = longToDate(CalendarTools.getNowDateTime().toDate().time)
            if (start > end) {
                throw WebException("开始日期不能大于结束日期")
            }
            if (end >= nowDay) {
                throw WebException("结束日期必须早于当前")
            }
            ResponseEntity.ok(
                logFileService.fileList(
                    CalendarTools.getDateTimeString(start, CalendarTools.DATE_FORMAT),
                    CalendarTools.getDateTimeString(end, CalendarTools.DATE_FORMAT)
                )
            )
        } catch (e: Exception) {
            logAdapter.error(e.message, e)
            throw WebException(e.message)
        }

    @Operation(summary = "日志文件下载", description = "下载指定的日志文件")
    @ApiResponses(ApiResponse(responseCode = "200", description = "请求成功，进行文件下载；"))
    @GetMapping(value = [LogApi.logFile], produces = [MediaType.ALL_VALUE])
    @Throws(WebException::class)
    fun downloadFile(
        request: HttpServletRequest, response: HttpServletResponse,
        @Parameter(description = "文件内容", required = true)
        @NotBlank(message = "文件内容不能为空")
        @RequestParam(name = "params") params: String
    ) {
        val fileContent = objectMapper.readValue(params, FileDownLoadPo::class.java)
        if (CommonTools.isNullStr(fileContent.fileName)) {
            throw WebException("文件名称不能为空")
        }
        val fileName =
            String(Base64.decode(fileContent.fileName), CommonTools.getDefaultCharset()).replace(
                "/",
                File.separator
            ).replace("\\", File.separator)
        logFileService.doDownLoadFile(request, response, fileName)
    }

}
